package br.com.winesys.geta.databinding.component.table;

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.print.PrinterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.GroupLayout;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.LayoutStyle;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.border.EmptyBorder;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.RowSorterListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableRowSorter;
import br.com.winesys.geta.base.ExceptionMessage;
import br.com.winesys.geta.databinding.component.listeners.SelectionListener;
import br.com.winesys.geta.editor.Editor;

public abstract class TableViewer<Entity> extends JPanel {

	private Editor<?> filter;
	private JPanel findPanel;
	private JLabel findLabel;
	private JTextField findText;
	private JScrollPane scroll;
	private JTable table;
	private TableRowSorter<TableModel<Entity>> sorter;
	private TableModel<Entity> tableModel;
	private boolean doSort = true;

	public TableViewer() {
		this(JTable.AUTO_RESIZE_NEXT_COLUMN, true);
	}

	public TableViewer(int tableResizeMode) {
		this(tableResizeMode, true);
	}

	public TableViewer(int tableResizeMode, boolean createFindPanel) {
		this.setLayout(new BorderLayout());
		this.filter = this.createFilter();
		if (this.filter != null) {
			this.add(this.filter.getComponent(), BorderLayout.NORTH);
		}
		if (createFindPanel) {
			this.findPanel = this.createFindPanel();
		}
		if (this.findPanel != null) {
			this.add(this.findPanel, BorderLayout.PAGE_END);
		}
		this.scroll = new JScrollPane();
		this.tableModel = this.createTableModel();
		this.table = new JTable(this.tableModel, this.tableModel.getColumnModel());
		this.table.setShowGrid(false);
		this.table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		this.table.setFillsViewportHeight(true);
		this.table.getTableHeader().setBorder(new EmptyBorder(0, 0, 0, 0));
		this.table.getTableHeader().setReorderingAllowed(false);
		this.table.setAutoResizeMode(tableResizeMode);
		// this.table.addKeyListener(new KeyAdapter() {
		// @Override
		// public void keyPressed(KeyEvent evt) {
		// if (evt.getKeyCode() == KeyEvent.VK_TAB || evt.getKeyCode() ==
		// KeyEvent.VK_ENTER) {
		// evt.consume();
		// TableViewer.this.table.transferFocus();
		// }
		// }
		// });
		this.table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

			@Override
			public void valueChanged(ListSelectionEvent evt) {
				if (!evt.getValueIsAdjusting()) {
					ListSelectionModel model = (ListSelectionModel) evt.getSource();
					Entity object = TableViewer.this.getValue(model.getLeadSelectionIndex());
					TableViewer.this.fireItemSelected(object);
				}
			}
		});
		this.table.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent evt) {
				if (evt.getClickCount() >= 2) {
					evt.consume();
				}
			}
		});
		this.scroll.setViewportView(this.table);
		this.add(this.scroll, BorderLayout.CENTER);
		this.sorter = new TableRowSorter<TableModel<Entity>>(this.tableModel);
		this.sorter.addRowSorterListener(new RowSorterListener() {

			@Override
			public void sorterChanged(RowSorterEvent evt) {
				if (TableViewer.this.doSort) {
					TableViewer.this.doSort = false;
					sort();
					TableViewer.this.doSort = true;
				}
			}
		});
		this.table.setRowSorter(this.sorter);
		this.addAncestorListener(new AncestorListener() {

			@Override
			public void ancestorRemoved(AncestorEvent evt) {
			}

			@Override
			public void ancestorMoved(AncestorEvent evt) {
			}

			@Override
			public void ancestorAdded(AncestorEvent evt) {
				int width = evt.getAncestor().getWidth();
				for (int i = 0; i < TableViewer.this.tableModel.getColumnCount(); i++) {
					TableViewer.this.table.getColumnModel().getColumn(i)
							.setPreferredWidth((width * TableViewer.this.tableModel.getColumnWidth(i)) / 100);
					// TableViewer.this.table.getColumnModel().getColumn(i).setPreferredWidth(TableViewer.this.tableModel.getColumnWidth(i));
				}
			}
		});
	}

	public List<Entity> getVisibleValues() {
		List<Entity>  resultado = new ArrayList<Entity>(this.table.getRowSorter().getViewRowCount());
		for (int i = 0; i < this.table.getRowSorter().getViewRowCount(); i++) {
			resultado.add(this.tableModel.getValue(this.table.getRowSorter().convertRowIndexToModel(i)));
		}
		return resultado;
	}

	protected abstract Editor<?> createFilter();

	protected abstract TableModel<Entity> createTableModel();

	protected JPanel createFindPanel() {
		JPanel panel = new JPanel();
		this.findLabel = new JLabel();
		this.findText = new JTextField();
		this.findLabel.setText("Localizar:");
		GroupLayout panelLayout = new GroupLayout(panel);
		panel.setLayout(panelLayout);
		panelLayout.setHorizontalGroup(panelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup(
				panelLayout.createSequentialGroup().addComponent(this.findLabel).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.findText, GroupLayout.DEFAULT_SIZE, 364, Short.MAX_VALUE)));
		panelLayout.setVerticalGroup(panelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGroup(
						panelLayout
								.createSequentialGroup()
								.addGap(5, 5, 5)
								.addGroup(
										panelLayout
												.createParallelGroup(GroupLayout.Alignment.BASELINE)
												.addComponent(this.findLabel)
												.addComponent(this.findText, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
														GroupLayout.PREFERRED_SIZE))));
		this.findText.getDocument().addDocumentListener(new DocumentListener() {

			@Override
			public void changedUpdate(DocumentEvent e) {
				sort();
			}

			@Override
			public void insertUpdate(DocumentEvent e) {
				sort();
			}

			@Override
			public void removeUpdate(DocumentEvent e) {
				sort();
			}
		});
		return panel;
	}

	private void sort() {
		try {
			RowFilter<TableModel<Entity>, Object> rf = null;
			int column = 0;
			if (this.sorter.getSortKeys().size() > 0) {
				column = this.sorter.getSortKeys().get(0).getColumn();
			}
			rf = RowFilter.regexFilter(this.findText != null ? this.findText.getText() : "", column);
			this.sorter.setRowFilter(rf);
		} catch (Exception ex) {
			JOptionPane.showMessageDialog(this, ExceptionMessage.getMessage("Erro executando ordenaçao", ex), "Erro", JOptionPane.ERROR_MESSAGE);
		}
	}

	public void addSelectionListener(SelectionListener<?> listener) {
		this.listenerList.add(SelectionListener.class, listener);
	}

	public void removeSelectionListener(SelectionListener<?> listener) {
		this.listenerList.remove(SelectionListener.class, listener);
	}

	private void fireItemSelected(Entity object) {
		for (SelectionListener<Entity> listener : this.listenerList.getListeners(SelectionListener.class)) {
			listener.itemSelected(object);
		}
	}

	@Override
	public synchronized void addMouseListener(MouseListener listener) {
		this.table.addMouseListener(listener);
	}

	@Override
	public synchronized void removeMouseListener(MouseListener listener) {
		this.table.removeMouseListener(listener);
	}

	public void setAutoResizeMode(int mode) {
		this.table.setAutoResizeMode(mode);
	}

	public void setTableHeader(JTableHeader header) {
		this.table.setTableHeader(header);
	}

	public void setValues(List<Entity> values) {
		this.tableModel.setValues(values);
		this.tableModel.fireTableDataChanged();
		this.table.getSelectionModel().clearSelection();
	}

	public List<Entity> getValues() {
		return this.tableModel.getValues();
	}

	public void print() throws PrinterException {
		this.table.print();
	}

	public Map<String, Object> getFilterProperties() {
		if (this.filter == null) {
			return new HashMap<String, Object>();
		}
		return this.filter.getProperties();
	}

	public Entity getValue(int index) {
		if (index >= 0 && this.tableModel.getRowCount() > index - 1) {
			try {
				index = this.table.convertRowIndexToModel(index);
				return this.tableModel.getValue(index);
			} catch (Exception ex) {
			}
		}
		return null;
	}

	public void addValue(Entity obj) {
		this.tableModel.addValue(obj);
	}

	public void select(Entity value) {
		int index = this.tableModel.indexOf(value);
		this.table.clearSelection();
		if (index >= 0) {
			this.table.setRowSelectionInterval(index, index);
			this.table.requestFocus();
		}
	}
}
